//////////////////////////////////////////////////////////////////
// MattiseOS Kernel
// Date: 2008
// Author: Matthew Iselin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////

// Includes
#include <mattise.h>
#include <stdint.h>
#include <mem/util.h>
#include <kernio/io.h>
#include <process/common.h>

// Functions

// Puts a process onto the wait queue, where it can only be woken
// by another process signalling it. Blocks until woken.
void wait()
{
	// get the cpu flags
	uint32_t int_state = 0;
	asm volatile( "pushf; popl %0" : "=r" (int_state) );

	// clear the interrupt flag while we work with the queues
	asm volatile( "cli" );

	// grab this entry
	struct pentry* ent = currprocess->ent;

	// unlink from the current queue
	if(ent->prev)
		ent->prev->next = ent->next;
	else
	{
		// if the previous is null then this is actually the first item
		// on the already queue, so we need to fix that
		if(ent == p_alreadyq)
			p_alreadyq = ent->next;
		else if(ent == p_readyq)
			p_readyq = ent->next;
		else if(ent == p_deadq)
			while(1) kSchedule(0);
		else if(ent == p_waitq)
			dprintf("== [WARN ] Already on the wait queue! ==\n");
	}
	if(ent->next)
		ent->next->prev = ent->prev;

	// link into the wait queue
	if(p_waitq == 0)
	{
		p_waitq = ent;
		ent->prev = ent->next = 0;
	}
	else
	{
		p_waitq->prev = ent;
		ent->prev = 0;
		ent->next = p_waitq;
		p_waitq = ent;
	}

	// go to a ready context
	kSchedule(0);

	// done with tables and global pointers
	if(int_state & 0x200)
		asm volatile("sti");
}

// Signals a process to wake up.
void wake(struct context* proc)
{
	// get the cpu flags
	uint32_t int_state = 0;
	asm volatile( "pushf; popl %0" : "=r" (int_state) );

	// clear the interrupt flag while we work with the queues
	asm volatile( "cli" );

	// grab this entry
	struct pentry* ent = proc->ent;

	// unlink from the current queue
	if(ent->prev)
		ent->prev->next = ent->next;
	if(ent->next)
		ent->next->prev = ent->prev;

	// link into the ready queue
	if(p_readyq == 0)
	{
		p_readyq = ent;
		ent->prev = ent->next = 0;
	}
	else
	{
		p_readyq->prev = ent;
		ent->prev = 0;
		ent->next = p_readyq;
		p_readyq = ent;
	}

	// done with tables and global pointers
	if(int_state & 0x200)
		asm volatile("sti");
}
